﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace DarkBone
{
   using System.ComponentModel;
   using System.Diagnostics;
   using System.IO;

   public class StormWrapper
    {
        [DllImport("kernel32.dll", EntryPoint = "GetLastError", ExactSpelling = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = false)]
        private static extern Int32 GetLastError();

        /// <summary>
        /// Function SFileSetLocale sets preferred language version of files that will be opened. From the moment of call, all atempts to open a file from archives will try the language version first. If required version will not be found, open function uses languale neutral function.
        /// </summary>
        /// <param name="lcLocale">[in] Identifier of language version. Function does not test validity of the parameter.</param>
        /// <returns>Function always succeeds and returns lcLocale.</returns>
        [DllImport("StormLib.dll", EntryPoint = "SFileSetLocale", ExactSpelling = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.AnsiBStr)]
        public static extern LCIDConversionAttribute SFileSetLocale(LCIDConversionAttribute lcLocale);

        /// <summary>
        /// Function SFileGetLocale simply returns the currently selected locale ID.
        /// </summary>
        /// <returns>Function always succeeds and returns the currently selected locale ID.</returns>
        [DllImport("StormLib.dll", EntryPoint = "SFileGetLocale", ExactSpelling = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.AnsiBStr)]
        public static extern LCIDConversionAttribute SFileGetLocale();

        /// <summary>
        /// Function SFileOpenArchive opens a MPQ archive for reading.
        /// </summary>
        /// <param name="szMpqName">[in] Archive file name to open.</param>
        /// <param name="dwPriority">[in] Priority of the archive for later search. StormLib does not use this parameter, set it to zero</param>
        /// <param name="dwFlags">[in] Flags that specify conditions for opening the archive. They can have these values:
        /// SFILE_OPEN_HARD_DISK_FILE - This value is used by Starcraft but does not seem to do anything different than any other values. If this is used, the function will open the archive without regard to the drive type it resides on.
        /// SFILE_OPEN_CD_ROM_FILE - This causes the function to only open the archive only if it is on a CD-ROM.
        /// StormLib does not use this parameter, set it to zero.</param>
        /// <param name="phMPQ">[out] Pointer to a variable of HANDLE type, where the opened archive handle will be stored.</param>
        /// <returns>When the function succeeds, it returns nonzero and phMPQ contains the handle of the opened archive. When the archive cannot be open, function returns FALSE. More informations about the error can be obtained from return value of GetLastError().</returns>
        [DllImport("StormLib.dll", EntryPoint = "SFileOpenArchive", ExactSpelling = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern Boolean SFileOpenArchive(String szMpqName, UInt32 dwPriority, UInt32 dwFlags, out IntPtr phMPQ);

        /// <summary>
        /// Function SFileCloseArchive closes an open archive.
        /// </summary>
        /// <param name="phMPQ">[in] Handle of open archive. Must be a handle obtained by a call of SFileOpenArchive or SFileCreateArchiveEx.</param>
        /// <returns>When the function succeeds, it returns nonzero and the arhive is closed. If fails, function returns FALSE. More informations about the error can be obtained from return value of GetLastError().</returns>
        /// If the archive was modified since it was open, the function implemented in StormLib also updates the MPQ header, hash table and block table.
        [DllImport("StormLib.dll", EntryPoint = "SFileCloseArchive", ExactSpelling = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern Boolean SFileCloseArchive(IntPtr phMPQ);

        /// <summary>
        /// Function SFileOpenFileEx opens a file from within an archive.
        /// </summary>
        /// <param name="phMPQ">[in] Handle of open archive. Must be a handle obtained by a call of SFileOpenArchive or SFileCreateArchiveEx.</param>
        /// <param name="szFileName">[in] Name of a file, which has to be open. The version implemented by StormLib supports also opening a file by its ordinal number (so its not possible to know the file name).</param>
        /// <param name="dwSearchScope">[in] Tells the search scope for the file. Can be these values:
        /// 0 - Search the file only in the MPQ archive given by hMPQ
        /// 1 - Search the file also in other open archives (ignored by StormLib).
        /// -1 - Open local hard disk file</param>
        /// <param name="phFile">[in] A pointer to a HANDLE variable, which receives the handle of the open file.</param>
        /// <returns>When the function succeeds, it returns nonzero and phFile contains handle of open file. If fails, function returns FALSE. More informations about the error can be obtained from return value of GetLastError().</returns>
        [DllImport("StormLib.dll", EntryPoint = "SFileOpenFileEx", ExactSpelling = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern Boolean SFileOpenFileEx(IntPtr phMPQ, String szFileName, UInt32 dwSearchScope, ref IntPtr phFile);

        /// <summary>
        /// Function SFileCloseFile closes an archived file.
        /// </summary>
        /// <param name="hFile">[in] Handle of an open file. Must be a handle obtained by a call of SFileOpenFileEx</param>
        /// <returns>When the function succeeds, it returns nonzero and the file is closed. If fails, function returns FALSE. More informations about the error can be obtained from return value of GetLastError().</returns>
        [DllImport("StormLib.dll", EntryPoint = "SFileCloseFile", ExactSpelling = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern Boolean SFileCloseFile(IntPtr hFile);

        /// <summary>
        /// Function SFileGetFileSize returns the size of an open file.
        /// </summary>
        /// <param name="hFile">[in] Handle of open file. Must be a handle obtained by a call of SFileOpenFileEx</param>
        /// <param name="pdwFileSizeHigh">[out] Pointer to a variable of DWORD type, which receives upper 32 bits of thefile size. Can be NULL.</param>
        /// <returns>When the function succeeds, it returns the file size. If fails, return value is 0xFFFFFFFF. More informations about the error can be obtained from return value of GetLastError().</returns>
        /// Upper 32 bits (the pdwFileSizeHigh parameter) isnot used by both Storm.dll and StormLib. When the pointer is valid, the function fills the variable with zero.
        [DllImport("StormLib.dll", EntryPoint = "SFileGetFileSize", ExactSpelling = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.U4)]
        public static extern UInt32 SFileGetFileSize(IntPtr hFile, out UInt32 pdwFileSizeHigh);

        /// <summary>
        /// Function SFileSetFilePointer sets a new position of file pointer.
        /// </summary>
        /// <param name="hFile">[in] Handle of open file. Must be a handle obtained by a call of SFileOpenFileEx</param>
        /// <param name="lFilePos">[in] The new file position.</param>
        /// <param name="plFilePosHigh">[in, out] Pointer to a LONG value, which receives upper 32 bits of movement offset.</param>
        /// <param name="dwMethod">[in] Relative point, which the movement is relative to. Can have the following values
        /// FILE_BEGIN - The movement will be relative to the file begin
        /// FILE_CURRENT - The movement will be relative to current file position
        /// FILE_END - The movement will be relative to the file ends</param>
        /// <returns>When the function succeeds, it returns the new file position. If fails, it returns 0xFFFFFFFF. More informations about the error can be obtained from return value of GetLastError().</returns>
        [DllImport("StormLib.dll", EntryPoint = "SFileSetFilePointer", ExactSpelling = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern Boolean SFileSetFilePointer(IntPtr hFile, UInt64 lFilePos, ref UInt64 plFilePosHigh, UInt32 dwMethod);

        /// <summary>
        /// Function SFileReadFile reads data from an open archived file.
        /// </summary>
        /// <param name="phMPQ">[in] Handle of open file. Must be a handle obtained by a call of SFileOpenFileEx</param>
        /// <param name="pBuffer">[in] Pointer to a buffer which receives the read data.</param>
        /// <param name="dwToRead">[in] Number of bytes which shall be read from the file.</param>
        /// <param name="pdwRead">[out] When not NULL, the function stores number of bytes actually read to the variable pointed by this parameter.</param>
        /// <param name="lpOverlapped">[out] Use this parameter for asynchronous file reading (NT systems only). StormLib ignores this parameter when reads data from an archived file.</param>
        /// <returns>When the function succeeds, it returns nonzero. Buffer pointed by pBuffer contains the read bytes and pdwRead contains number of bytes read. If fails, function returns FALSE. More informations about the error can be obtained from return value of GetLastError().</returns>
        /// When the file does not contain required number of bytes, function returns FALSE, but pdwRead contains the number of bytes really loaded into the buffer.
        [DllImport("StormLib.dll", EntryPoint = "SFileReadFile", ExactSpelling = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern Boolean SFileReadFile(IntPtr phMPQ, IntPtr pBuffer, UInt32 dwToRead, out UInt32 pdwRead, IntPtr /*System.Threading.NativeOverlapped*/ lpOverlapped);


        /// <summary>
        /// Function SFileExtractFile extract complete file from an archive. It is a "helper", useful in nearly each application working with MPQs.
        /// </summary>
        /// <param name="phMPQ">[in] Handle of open archive. Must be a handle obtained by a call of SFileOpenArchive or SFileCreateArchiveEx</param>
        /// <param name="szToExtract">[in] Pointer to name of archived file which is to be extracted.</param>
        /// <param name="szExtracted">[in] Pointer to name of local file, into which will be the content of the file written. If the file already exists, it will be overwritten.</param>
        /// <returns>When the function succeeds, it returns nonzero. In this case, the file has been successfully created and its content successfully written. If an error, function returns FALSE. More informations about the error can be obtained from return value of GetLastError().</returns>
        [DllImport("StormLib.dll", EntryPoint = "SFileExtractFile", ExactSpelling = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern Boolean SFileExtractFile(IntPtr hMpq, String szToExtract, String szExtracted);

        /// <summary>
        /// Function SFileCreateArchiveEx opens or creates an archive file. It can also convert an existing file to MPQ archive. After the archive is open/created, it is accessible for reading and writing.
        /// </summary>
        /// <param name="szMpqName">[in] Name of an archive, which is to be open.</param>
        /// <param name="dwCreationDisposition">Specifies, what the function should exactly do. The following table specifies the parameter values
        /// CREATE_NEW - Creates new MPQ archive || Returns error
        /// CREATE_ALWAYS - Creates new MPQ archive || Existing file will be overwritten and function creates a new MPQ archive.
        /// OPEN_EXISTING - Returns error || Opens the archive
        /// OPEN_ALWAYS - Opens archive or converts the file to MPQ archive || Opens the archive</param>
        /// <param name="dwHashTableSize">[in] When a new archive will be created, this parameter contains hash table size for the new archive. Value of this parameter is corrected to be the nearest greater power of two in the range <0x2; 0x40000>. When opening existing archive, the parameter is ignored.</param>
        /// <param name="phMPQ">[out] A pointer to a HANDLE variable, where the handle of created/open archive will be stored.</param>
        /// <returns>When the function succeeds, it returns nonzero and phMPQ contains the handle of open archive. When the archive cannot be open/created, function returns FALSE. More informations about the error can be obtained from return value of GetLastError().</returns>
        [DllImport("StormLib.dll", EntryPoint = "SFileCreateArchiveEx", ExactSpelling = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern Boolean SFileCreateArchiveEx(String szMpqName, UInt32 dwCreationDisposition, UInt32 dwHashTableSize, out IntPtr phMPQ);

        /// <summary>
        /// Function SFileAddFile adds a file into the archive. The archive must be open for writing by SFileCreateArchiveEx.
        /// </summary>
        /// <param name="hMPQ">[in] Handle of open archive. Must be a handle obtained by a call of SFileCreateArchiveEx</param>
        /// <param name="szFileName">[in] Name of a file which is to be added into the archive.</param>
        /// <param name="szArchivedName">[in] Name under which the file will be stored in the archive.</param>
        /// <param name="dwFlags">[in] Bit mask, which controls the way of storing file in the archive
        /// MPQ_FILE_COMPRESS_PKWARE - The file will be compressed with PKWARE Data Compression Library.
        /// MPQ_FILE_COMPRESS_MULTI - The file will be compressed with the multiple compressions.
        /// MPQ_FILE_ENCRYPTED - The file will be encrypted.
        /// MPQ_FILE_FIXSEED - File encryption key will be fixed according to file name and file position in the archive.
        /// MPQ_FILE_REPLACEEXISTING - If the file already exists in the archive, it will be replaced.</param>
        /// <returns>Function returns nonzero, when the file was successfully added into the archive. On error, functions return FALSE. More informations about the error could be returned by GetLastError().</returns>
        /// It is not possible to combine flags MPQ_FILE_COMPRESS_PKWARE and MPQ_FILE_COMPRESS_MULTI. If the function is called with both flags, returns error.
        /// Locale ID of the file will be set to value recently set using SFileSetLocale.
        [DllImport("StormLib.dll", EntryPoint = "SFileAddFile", ExactSpelling = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern Boolean SFileAddFile(IntPtr hMPQ, String szFileName, String szArchivedName, UInt32 dwFlags);

        /// <summary>
        /// Function SFileAddWave adds a WAVE file into the archive. The archive must be open for writing by SFileCreateArchiveEx. Unlike SFileAddFile, function optimizes the compression for pattern of WAVE files, doing better compression than if they were added using SFileAddFile.
        /// </summary>
        /// <param name="hMPQ">[in] Handle of open archive. Must be a handle obtained by a call of SFileCreateArchiveEx</param>
        /// <param name="szFileName">[in] Name of a file which is to be added into the archive.</param>
        /// <param name="szArchivedName">[in] Name under which the file will be stored in the archive.</param>
        /// <param name="dwFlags">[in] Bit mask, which controls the way of storing file in the archive
        /// MPQ_FILE_COMPRESS_PKWARE - The file will be compressed with PKWARE Data Compression Library.
        /// MPQ_FILE_COMPRESS_MULTI - The file will be compressed with the multiple compressions.
        /// MPQ_FILE_ENCRYPTED - The file will be encrypted.
        /// MPQ_FILE_FIXSEED - File encryption key will be fixed according to file name and file position in the archive.
        /// MPQ_FILE_REPLACEEXISTING - If the file already exists in the archive, it will be replaced.</param>
        /// <param name="dwQuality">[in] Bit mask, which controls the quality of stored file in the archive
        /// MPQ_WAVE_QUALITY_HIGH - The file will be stored with the best quality (but will occupy the largest space)
        /// MPQ_WAVE_QUALITY_MEDIUM - Medium quality, medium compression
        /// MPQ_WAVE_QUALITY_LOW - Low quality, the best compression.</param>
        /// <returns>Function returns nonzero, when the file was successfully added into the archive. On error, functions return FALSE. More informations about the error could be returned by GetLastError().</returns>
        /// It is not possible to combine flags MPQ_FILE_COMPRESS_PKWARE and MPQ_FILE_COMPRESS_MULTI. If the function is called with both flags, returns error.
        /// Locale ID of the file will be set to value recently set using SFileSetLocale.
        [DllImport("StormLib.dll", EntryPoint = "SFileAddWave", ExactSpelling = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern Boolean SFileAddWave(IntPtr hMPQ, String szFileName, String szArchivedName, out UInt32 dwFlags, UInt32 dwQuality);

        /// <summary>
        /// Function SFileRemoveFile removes a file from the archive. The archive must be open for writing using SFileCreateArchiveEx.
        /// </summary>
        /// <param name="hMPQ">[in] Handle of open archive. Must be a handle obtained by a call of SFileCreateArchiveEx</param>
        /// <param name="szFileName">[in] Name of the file which has to be removed from the archive.</param>
        /// <returns>Function returns nonzero, if the file was successfully removed from the archive. On error returns FALSE. More informations about the error returns the GetLastError() function.</returns>
        [DllImport("StormLib.dll", EntryPoint = "SFileRemoveFile", ExactSpelling = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern Boolean SFileRemoveFile(IntPtr hMPQ, String szFileName);

        /// <summary>
        /// Function SFileRenameFile renames a file within MPQ archive. The archive must be open for writing using SFileCreateArchiveEx.
        /// </summary>
        /// <param name="hMPQ">[in] Handle of open archive. Must be a handle obtained by a call of SFileCreateArchiveEx.</param>
        /// <param name="szOldFileName">[in] Name of an existing file within the archive.</param>
        /// <param name="szNewFileName">[in] New name of that file. The file can have another directory name (since directories are virtual in MPQ archives).</param>
        /// <returns>Function returns nonzero, if the file was successfully renamed. On error returns FALSE. More informations about the error returns the GetLastError() function.</returns>
        [DllImport("StormLib.dll", EntryPoint = "SFileRenameFile", ExactSpelling = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern Boolean SFileRenameFile(IntPtr hMPQ, String szOldFileName, String szNewFileName);

        /// <summary>
        /// Functions SFileFindFirstFile and SFileFindFirstFileEx search files in MPQ archive. Because MPQ archives do not contain file names, these functions need file list for successfull search.
        /// </summary>
        /// <param name="hMPQ">[in] Handle of open archive. Must be a handle obtained by a call of SFileOpenArchive  or SFileCreateArchiveEx. Can be NULL, if the bCheckIfExist is set to FALSE.</param>
        /// <param name="szMask">[in] Search mask for the files. Example: "*.wav".</param>
        /// <param name="lpFindFileData">[out] Pointer to the SFILE_FIND_DATA structure which receives information about found file.</param>
        /// <param name="szFileList">[in] Name of a text file with file list. This file must contain file names with their full paths, one file per line. If this parameter is NULL, the function searches the internal listfile stored in the MPQ archive. hMPQ may not be NULL if szListFile is also NULL.</param>
        /// <returns>Function returns a search handle, if a match file was found in the archive. This search handle must be freed by calling SFileFindClose. On error returns NULL. More informations about the error returns the GetLastError() function.</returns>
        /// If there are more language versions of the same file, file search will return one entry for each language version as successfull search result.
        /// An application should correctly test if a file was not returned multiple times. Depending on the given file list, the function may not return all files in the archive and also can return one file multiple times.
        /// An application may not modify the structure during search.
        [DllImport("StormLib.dll", EntryPoint = "SFileFindFirstFile", ExactSpelling = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.SysUInt)]
        public static extern IntPtr SFileFindFirstFile(IntPtr hMPQ, String szMask, out SFILE_FIND_DATA lpFindFileData, String szFileList);

        /// <summary>
        /// Function SFileFindNextFile continues search began by SFileFindFirstFile(Ex).
        /// </summary>
        /// <param name="hFind">[in] Handle of search. Must be a handle obtained by a call of SFileFindFirstFile(Ex).</param>
        /// <param name="lpFindFileData">[out] Pointer to the SFILE_FIND_DATA structure which receives information about found file.</param>
        /// <returns>Function returns nonzero, if a matching file was successfully found in the archive. On error returns FALSE. More informations about the error returns the GetLastError() function.</returns>
        [DllImport("StormLib.dll", EntryPoint = "SFileFindNextFile", ExactSpelling = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern Boolean SFileFindNextFile(IntPtr hFind, out SFILE_FIND_DATA lpFindFileData);

        /// <summary>
        /// Function SFileFindClose ends the search started by SFileFindFirstFile(Ex). Must be used when the search is done.
        /// </summary>
        /// <param name="hFind">[in] Handle of search. Must be a handle obtained by a call of SFileFindFirstFile(Ex).</param>
        /// <returns>Function returns nonzero, if a matching file was successfully found in the archive. On error returns FALSE. More informations about the error returns the GetLastError() function.</returns>
        [DllImport("StormLib.dll", EntryPoint = "SFileFindClose", ExactSpelling = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern Boolean SFileFindClose(IntPtr hFind);

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct SFILE_FIND_DATA
        {
            [MarshalAs(
               UnmanagedType.ByValArray,
               SizeConst = 260)]
            public char[] cFileName;
            public String szPlainName;                      // Pointer to file part
            public UInt32 dwHashIndex;                 // Hash table index for the file
            public UInt32 dwBlockIndex;                // Block table index for the file
            public UInt32 dwFileSize;                  // File size in bytes
            public UInt32 dwFileFlags;                 // MPQ file flags
            public UInt32 dwCompSize;                  // Compressed file size
            public UInt32 dwFileTimeLo;                // Low 32-bits of the file time (0 if not present)
            public UInt32 dwFileTimeHi;                // High 32-bits of the file time (0 if not present)
            public LCIDConversionAttribute lcLocale;    // Locale version
        };

        public const UInt32 HASH_TABLE_SIZE_MIN = 0x00000002;
        public const UInt32 HASH_TABLE_SIZE_MAX = 0x00040000;

        public const UInt32 SFILE_OPEN_HARD_DISK_FILE = 0x00000002; // Open the archive on HDD
        public const UInt32 SFILE_OPEN_CDROM_FILE = 0x00000003; // Open the archive only if it is on CDROM

        // Values for SFileOpenFile
        public const UInt32 SFILE_OPEN_FROM_MPQ = 0x00000000; // Open the file from the MPQ archive
        public const UInt32 SFILE_OPEN_BY_INDEX = 0x00000001; // The 'szFileName' parameter is actually the file index
        public const UInt32 SFILE_OPEN_LOCAL_FILE = 0xFFFFFFFF; // Open the file from the MPQ archive

        // Flags for TMPQArchive::dwFlags
        public const UInt32 MPQ_FLAG_CHANGED = 0x00000001; // If set, the MPQ has been changed
        public const UInt32 MPQ_FLAG_PROTECTED = 0x00000002; // Set on protected MPQs (like W3M maps)

        // Flags for SFileOpenArchive
        public const UInt32 MPQ_OPEN_NO_LISTFILE         = 0x0010; // Don't load the internal listfile
        public const UInt32 MPQ_OPEN_NO_ATTRIBUTES       = 0x0020; // Don't open the attributes
        public const UInt32 MPQ_OPEN_FORCE_MPQ_V1        = 0x0040; // Always open the archive as MPQ v 1.00, ignore the "wFormatVersion" variable in the header
        public const UInt32 MPQ_OPEN_CHECK_SECTOR_CRC    = 0x0080; // On files with MPQ_FILE_SECTOR_CRC, the CRC will be checked when reading file
        public const UInt32 MPQ_OPEN_READ_ONLY           = 0x0100; // Open the archive for read-only access

        // Flags for SFileAddFile
        public const UInt32 MPQ_FILE_COMPRESS_PKWARE = 0x00000100; // Compression made by PKWARE Data Compression Library
        public const UInt32 MPQ_FILE_COMPRESS_MULTI = 0x00000200; // Multiple compressions
        public const UInt32 MPQ_FILE_COMPRESSED = 0x0000FF00; // File is compressed
        public const UInt32 MPQ_FILE_ENCRYPTED = 0x00010000; // Indicates whether file is encrypted 
        public const UInt32 MPQ_FILE_FIXSEED = 0x00020000; // File decrypt seed has to be fixed
        public const UInt32 MPQ_FILE_SINGLE_UNIT = 0x01000000; // File is stored as a single unit, rather than split into sectors (Thx, Quantam)
        public const UInt32 MPQ_FILE_DUMMY_FILE = 0x02000000; // The file is only 1 byte long and its name is a hash
        public const UInt32 MPQ_FILE_HAS_EXTRA = 0x04000000; // The file has extra data appended after regular data.
        // Must be with compressed files only
        public const UInt32 MPQ_FILE_EXISTS = 0x80000000; // Set if file exists, reset when the file was deleted
        public const UInt32 MPQ_FILE_REPLACEEXISTING = 0x80000000; // Replace when the file exist (SFileAddFile)

        // Compression types for multilpe compressions
        public const UInt32 MPQ_COMPRESSION_HUFFMANN = 0x0000001;    // Huffmann compression (used on WAVE files only)
        public const UInt32 MPQ_COMPRESSION_ZLIB = 0x0000002;    // ZLIB compression
        public const UInt32 MPQ_COMPRESSION_PKWARE = 0x0000008;    // PKWARE DCL compression
        public const UInt32 MPQ_COMPRESSION_BZIP2 = 0x0000010;    // BZIP2 compression
        public const UInt32 MPQ_COMPRESSION_WAVE_MONO = 0x0000040;    // 
        public const UInt32 MPQ_COMPRESSION_WAVE_STEREO = 0x0000080;    // 

        // Constants for SFileAddWave
        public const UInt32 MPQ_WAVE_QUALITY_HIGH = 0x0000000;      // Best quality, the worst compression
        public const UInt32 MPQ_WAVE_QUALITY_MEDIUM = 0x0000001;      // Medium quality, medium compression
        public const UInt32 MPQ_WAVE_QUALITY_LOW = 0x0000002;      // Low quality, the best compression

        public const UInt32 SFILE_INFO_ARCHIVE_SIZE = 0x0000001;      // MPQ size (value from header)
        public const UInt32 SFILE_INFO_HASH_TABLE_SIZE = 0x0000002;      // Size of hash table, in entries
        public const UInt32 SFILE_INFO_BLOCK_TABLE_SIZE = 0x0000003;      // Number of entries in the block table
        public const UInt32 SFILE_INFO_BLOCK_SIZE = 0x0000004;      // Size of file block (in bytes)
        public const UInt32 SFILE_INFO_HASH_TABLE = 0x0000005;      // Pointer to Hash table (TMPQHash *)
        public const UInt32 SFILE_INFO_BLOCK_TABLE = 0x0000006;      // Pointer to Block Table (TMPQBlock *)
        public const UInt32 SFILE_INFO_NUM_FILES = 0x0000007;      // Real number of files within archive
        public const UInt32 SFILE_INFO_HASH_INDEX = 0x0000008;      // Hash index of file in MPQ
        public const UInt32 SFILE_INFO_CODENAME1 = 0x0000009;      // The first codename of the file
        public const UInt32 SFILE_INFO_CODENAME2 = 0x000000A;      // The second codename of the file
        public const UInt32 SFILE_INFO_LOCALEID = 0x000000B;      // Locale ID of file in MPQ
        public const UInt32 SFILE_INFO_BLOCKINDEX = 0x000000C;      // Index to Block Table
        public const UInt32 SFILE_INFO_FILE_SIZE = 0x000000D;      // Original file size
        public const UInt32 SFILE_INFO_COMPRESSED_SIZE = 0x000000E;      // Compressed file size
        public const UInt32 SFILE_INFO_FLAGS = 0x000000F;      // File flags
        public const UInt32 SFILE_INFO_POSITION = 0x0000010;      // File position within archive
        public const UInt32 SFILE_INFO_SEED = 0x0000011;      // File decryption seed
        public const UInt32 SFILE_INFO_SEED_UNFIXED = 0x0000012;      // Decryption seed not fixed to file pos and size

        public const UInt32 SFILE_SEARCH_CURRENT_ONLY = 0x00000000;
        public const UInt32 SFILE_SEARCH_ALL_OPEN = 0x00000001;

        [Flags]
        public enum MPQ_FILE_VALID_FLAGS : uint
        {
            MPQ_FILE_COMPRESS_PKWARE,
            MPQ_FILE_COMPRESS_MULTI,
            MPQ_FILE_ENCRYPTED,
            MPQ_FILE_FIXSEED,
            MPQ_FILE_SINGLE_UNIT,
            MPQ_FILE_DUMMY_FILE,
            MPQ_FILE_HAS_EXTRA,
            MPQ_FILE_EXISTS,
        }

                                                                                                            
     public const UInt32 GENERIC_READ = 0x80000000;                                                         
     public const UInt32 GENERIC_WRITE = 0x40000000;                                                        
     public const UInt32 FILE_SHARE_READ = 0x00000001;                                                      
     public const UInt32 FILE_SHARE_WRITE = 0x00000002;                                                     
     public const UInt32 FILE_ATTRIBUTE_DIRECTORY = 0x00000010;                                             
                                                                                                                                                                                                                    
     public const UInt32 CREATE_NEW = 1;                                                                    
     public const UInt32 CREATE_ALWAYS = 2;                                                                 
     public const UInt32 OPEN_EXISTING = 3;                                                                 
     public const UInt32 OPEN_ALWAYS = 4;                                                                   
     public const UInt32 TRUNCATE_EXISTING = 5;                                                             
                                                                                                                                                                                                                                                                                   
                                                                                                            
                                                                                                            
//DllImport statements identify specific functions and declare their C# function signature                    
                                                                                                            
                                                                                                            
                                                                                                            
     [DllImport("kernel32.dll", SetLastError = true)]                                                       
     public static extern IntPtr CreateFile(                                                                
         string lpFileName,                                                                                 
         uint dwDesiredAccess,                                                                              
         uint dwShareMode,                                                                                  
         IntPtr lpSecurityAttributes,                                                                       
         uint dwCreationDisposition,                                                                        
         uint dwFlagsAndAttributes,                                                                         
         IntPtr hTemplateFile);                                                                             
                                                                                                            
                                                                                                            
                                                                                                            
     [DllImport("kernel32.dll", SetLastError = true)]                                                       
                                                                                                            
     [return: MarshalAs(UnmanagedType.Bool)]                                                                
                                                                                                            
     public static extern bool CloseHandle(                                                                 
                                                                                                            
         IntPtr hObject);                                                                                   
                                                                                                            
                                                                                                            
                                                                                                            
     [DllImport("kernel32.dll", SetLastError = true)]                                                       
                                                                                                            
     [return: MarshalAs(UnmanagedType.Bool)]                                                                
                                                                                                            
     public static extern bool GetFileInformationByHandle(                                                  
                                                                                                            
         IntPtr hFile,                                                                                      
                                                                                                            
         out BY_HANDLE_FILE_INFORMATION lpFileInformation);                                                 
                                                                                                            
                                                                                                            
                                                                                                            
     [DllImport("kernel32.dll", SetLastError = true)]                                                       
                                                                                                            
     [return: MarshalAs(UnmanagedType.Bool)]                                                                
                                                                                                            
     public static extern bool DeleteFile(                                                                  
                                                                                                            
         string fileName);                                                                                  
                                                                                                            
                                                                                                            
                                                                                                            
     [DllImport("kernel32.dll")]                                                                            
                                                                                                            
     [return: MarshalAs(UnmanagedType.Bool)]                                                                
                                                                                                            
     public static extern bool ReadFile(                                                                    
                                                                                                            
         IntPtr hFile,                                                                                      
                                                                                                            
         IntPtr lpBuffer,                                                                                   
                                                                                                            
         uint nNumberOfBytesToRead,                                                                         
                                                                                                            
         out uint lpNumberOfBytesRead,                                                                      
                                                                                                            
         IntPtr lpOverlapped);                                                                              
                                                                                                            
                                                                                                            
                                                                                                            
     [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]                            
                                                                                                            
     [return: MarshalAs(UnmanagedType.Bool)]                                                                
                                                                                                            
     public static extern bool WriteFile(                                                                   
                                                                                                            
         IntPtr hFile,                                                                                      
                                                                                                            
         IntPtr bytes,                                                                                      
                                                                                                            
         uint nNumberOfBytesToWrite,                                                                        
                                                                                                            
         out uint lpNumberOfBytesWritten,                                                                   
                                                                                                            
         int overlapped);                                                                                   
                                                                                                            
                                                                                                            
                                                                                                            
     [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]                            
                                                                                                            
     [return: MarshalAs(UnmanagedType.Bool)]                                                                
                                                                                                            
     public static extern bool WriteFile(                                                                   
                                                                                                            
         IntPtr hFile,                                                                                      
                                                                                                            
         byte[] lpBuffer,                                                                                   
                                                                                                            
         uint nNumberOfBytesToWrite,                                                                        
                                                                                                            
         out uint lpNumberOfBytesWritten,                                                                   
                                                                                                            
         int overlapped);                                                                                   
                                                                                                            
                                                                                                            
                                                                                                            
     [DllImport("kernel32.dll")]                                                                            
                                                                                                            
     public static extern void ZeroMemory(IntPtr ptr, int size);                                            
                                                                                                            
                                                                                                            
//This section declares the structures used by the Win32 functions so that the information can be accessed by 
                                                                                                            
                                                                                                            
                                                                                                            
     [StructLayout(LayoutKind.Sequential, Pack = 1)]                                                        
                                                                                                            
     public struct BY_HANDLE_FILE_INFORMATION                                                               
                                                                                                            
     {                                                                                                      
                                                                                                            
         public uint FileAttributes;                                                                        
                                                                                                            
         public FILETIME CreationTime;                                                                      
                                                                                                            
         public FILETIME LastAccessTime;                                                                    
                                                                                                            
         public FILETIME LastWriteTime;                                                                     
                                                                                                            
         public uint VolumeSerialNumber;                                                                    
                                                                                                            
         public uint FileSizeHigh;                                                                          
                                                                                                            
         public uint FileSizeLow;                                                                           
                                                                                                            
         public uint NumberOfLinks;                                                                         
                                                                                                            
         public uint FileIndexHigh;                                                                         
                                                                                                            
         public uint FileIndexLow;                                                                          
                                                                                                            
     }                                                                                                      
                                                                                                            
                                                                                                            
                                                                                                            
     [StructLayout(LayoutKind.Sequential, Pack = 1)]                                                        
                                                                                                            
     public struct FILETIME                                                                                 
                                                                                                            
     {                                                                                                      
                                                                                                            
         public uint DateTimeLow;                                                                           
                                                                                                            
         public uint DateTimeHigh;                                                                          
                                                                                                            
     }                                                                                                      
                                                                                                            

        /// <summary>
        /// Reads the MPQ mapname.
        /// </summary>
        /// <param name="filename">The filename.</param>
        /// <returns></returns>
        public static string ReadMPQMapname(String filename)
        {
            IntPtr handleMpq;
            if (SFileOpenArchive(filename, SFILE_OPEN_HARD_DISK_FILE, SFILE_SEARCH_CURRENT_ONLY | MPQ_OPEN_READ_ONLY, out handleMpq))
            {
               var hFile = IntPtr.Zero;
               string returnstring = string.Empty;
               IntPtr buffer = IntPtr.Zero;
               try
               {
                  SFileOpenFileEx(handleMpq, @"enUS.SC2Data\LocalizedData\GameStrings.txt", SFILE_OPEN_FROM_MPQ, ref hFile);
                   if (-1 != hFile.ToInt32())
                   {
                      
                       buffer = Marshal.AllocHGlobal(4096 * sizeof(char));
                       ZeroMemory(buffer, 4096 * sizeof(char));
                       uint nBytes;
                       bool bRtn = SFileReadFile(hFile, buffer, 4096 * sizeof(char), out nBytes, IntPtr.Zero);
                        if (nBytes > 0)
                        {
                           returnstring = Marshal.PtrToStringAnsi(buffer);
                        }
                        else
                        {
                              throw new Exception("ReadFile() returned true but read zero bytes");
                        }
                   }
                   else
                   {
                       Exception excptn = new Win32Exception(Marshal.GetLastWin32Error());
                       if (!excptn.Message.Contains("cannot find the file"))
                       {
                           throw excptn;
                       }
                   }
               }
               catch (Exception exception)
               {
                  Debug.WriteLine(exception);
               }
               finally
               {
                   if (hFile != IntPtr.Zero)
                   {
                      SFileCloseFile(hFile);
                   }
                   hFile = IntPtr.Zero;
                  if (handleMpq != IntPtr.Zero)
                  {
                     SFileCloseArchive(handleMpq);
                  }
                  if (buffer != IntPtr.Zero)
                   {
                       Marshal.FreeHGlobal(buffer);
                   }
                   GC.Collect();
               }

               var ind = returnstring.IndexOf("DocInfo/Name=");
               var mapname = string.Empty;
               for (int i = ind + 13; ; i++)
               {
                  if ((returnstring[i] == '\r') || (returnstring[i] == '\n')) break;
                  mapname += returnstring[i];
               }
               return mapname;

            }
            else
            {
                  throw new Exception("ERRORNUM: " + GetLastError().ToString());
            }
        }

       /*
DocInfo/Author=Blizzard Entertainment
DocInfo/DescLong=The two main paths to the enemy are through high ground expansions. Control these to gain a positional and resource advantage. Use the Xel'Naga Towers to spot enemy movement through the alternate low ground routes.
DocInfo/DescShort=1v1
DocInfo/Name=Agria Valley
MapInfo/Player00/Name=Neutral
MapInfo/Player01/Name=Player 1
MapInfo/Player02/Name=Player 2
Variant001/Desc=Two players fight to the death.
Variant001/Name=Melee 1v1
        */
    }
}
